/*
 * @filename: mainwindow.cpp
 * @desc:处理链接请求、执行socket网络读写、调用人脸数据库、员工数据库、考勤数据库。
 * @detail:接受客户端的连接请求，
*/
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "opencv2/opencv.hpp"

#include <QDebug>
#include <QSqlRecord>
#include <QJsonDocument>
#include <QJsonObject>
#include <QThread>

#include "global_config.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);


    //model.setTable("employee");//设置表格名称(不知为何，不能正常查询）
    model = new QSqlTableModel(this);
    model->setTable("employee");//设置表格名称

    //1. 配置服务器网路参数，并设置监听的Reactor
    m_server = new QTcpServer(this);
    connect(m_server, &QTcpServer::newConnection, this, &MainWindow::accpet_clt);
    m_server->listen(QHostAddress::Any,SERVER_PORT);

    buf_size = 0;

    //2. 将人脸查询对象放进子线程之中，并连接相关的信号
    m_faceop = new FaceObject();
    m_thread = new QThread();
    m_faceop->moveToThread(m_thread);

    //connect(this, SIGNAL(ask_faceid(cv::Mat&)), m_faceop, SLOT(query_face(cv:Mat &)));//报错：Object::connect: No such slot FaceObject::query_face
    connect(this, &MainWindow::ask_faceid, m_faceop, FaceObject::query_face);
    connect(m_faceop, SIGNAL(query_done(const int)), this, SLOT(get_faceid(const int)));
    m_thread->start();//启动子线程（此时子线程并未真正运行，还要具体的信号）
}

MainWindow::~MainWindow()
{
    delete ui;
}



void MainWindow::accpet_clt()
{
    m_socket = m_server->nextPendingConnection();
    //这里最好显示以下客户端的相关信息

    //设置读写回调函数
    connect(m_socket, &QTcpSocket::readyRead, this, &MainWindow::rcv_from_clt);
}


/* SLOT
 * @func:接收到客户端发送过来的图片数据
 * @related-param: buf_size
*/
void MainWindow::rcv_from_clt()
{
    //0.定义要接收的数据的缓存
    QByteArray rcv_img_buf;

    //1. 初始化stream_in
    //QByteArray rcv_ba_buf;
    //QDataStream st_i(rcv_ba_buf);
    QDataStream st_i(m_socket);//将socket的缓存区和stream绑定起来
    st_i.setVersion(QDataStream::Qt_5_14);//Qt版本:5.14.2

    //2. 开始接收数据(接收数据原则：先判断socket缓存区字节大小，满足要求之后再进行接收)
    if(buf_size == 0)
    {
        //缓存字节不满足要求
        if(m_socket->bytesAvailable() < (qint64)sizeof(buf_size))
        {
            return;
        }
        st_i>>buf_size;//先接收数据大小（接收成功后再接受具体的数据）
        qDebug() <<"buf_size=" << buf_size;
    }

    //3. 接收到buf_size 后，继续接收图片数据
    //首先判断缓存字节满不满足要求？不满足就返回。
    if(m_socket->bytesAvailable() < (qint64)buf_size)
    {
        qDebug() <<"available not qualified!:" <<m_socket->bytesAvailable();
        return;
    }

    //4.如果缓存字节满足要求,接收数据
    st_i>>rcv_img_buf;
    buf_size = 0; //重置buf_size以接收下一次数据

    //检查接收数据的有效性
    if(rcv_img_buf.size() == 0)
    {
        return;
    }

    //5. 将接收到的字节流还原为图片格式，然后显示在 showLbl上
    QPixmap img;
    img.loadFromData(rcv_img_buf, "jpg");//关键的函数
    img = img.scaled(ui->showLbl->width(),ui->showLbl->height());

    ui->showLbl->setPixmap(img);

    //6.根据人脸查询人脸数据库,得到人脸的face_id
    //6-1 将传过来的数据转换为cv::Mat格式
    std::vector<uchar> decode_buf;
    decode_buf.resize(rcv_img_buf.size());//为什么要resize呢？
    memcpy(decode_buf.data(), rcv_img_buf.data(), rcv_img_buf.size());
    cv::Mat decode_img = cv::imdecode(decode_buf,cv::IMREAD_COLOR);


    //6-2 根据人脸数据库，查询face_id.
    emit ask_faceid(decode_img);//发出请求face_id信号，转到FaceObject的槽函数query_face
    //int face_id =  face_op.query_face(decode_img);//运行到此处程序处于卡顿的状态
    qDebug()<<"释放查询信号成功,等待查询结果.....";//等到face_id返回之后，转到自己的槽函数get_faceid中，
}


/*
 * SLOT
 * @func:获取到face_id之后,查询员工数据库信息，将员工详细信息（姓名、部门等）返回给客户端。
 * @param:face_id
*/
void MainWindow::get_faceid(const int face_id)
{
    //1. 打印出face_id，检查有效性。
    qDebug() << "face_id ="<<face_id;
    if(face_id < 0)
    {
        QString strJson =QString("{\"name\":\" \",\"emy_id\":\" \",\"part\":\" \"}");
        qDebug() << strJson;
        m_socket->write(strJson.toUtf8());
    }
    //2.查询employee表，得到员工的相关信息(工号、姓名、部门、考勤时间）（待加：人脸）
    //数据组织成json格式，发送给客户端
    model->setFilter(QString("face_id=%1").arg(face_id));//过滤器(以face_id为查询依据）
    model->select();
    if(model->rowCount()>0)
    {
        //model.select();//不是在这里

        //只有一条数据，因此为record(0)
        QSqlRecord rcd = model->record(0);
        if(rcd.isEmpty())
        {
            qDebug()<<"Record is empty";
            return;
        }

        int epy_id = rcd.value("emy_id").toInt();
        QString part = rcd.value("part").toString();
        QString name = rcd.value("name").toString();

        //将这些数据组织成Json，然后发送出去 "epy_id=" "part=" "name="
        //create_json(epy_id, name, part);//较为正确的做法：利用QJson插入记录
        //我自己的办法:自己写json（注意：应该用冒号，等号是错误的）
        //QString strJson =QString("{\"name\"=\"%1\",\"emy_id\"=\"%2\",\"part\"=\"%3\"}").arg(name).arg(epy_id).arg(part);
        QString strJson =QString("{\"name\":\"%1\",\"emy_id\":\"%2\",\"part\":\"%3\"}").arg(name).arg(epy_id).arg(part);
        qDebug() << strJson;
        m_socket->write(strJson.toUtf8());
    }
    else
    {
        qDebug() << "Cannot find in DB, model row_cnt =" <<model->rowCount();
    }
}

void MainWindow::create_json(int epy_id, QString &name, QString &part)
{
    // 构建 JSON 对象
    QJsonObject json;
    json.insert("emy_id", epy_id);
    json.insert("name", name);
    json.insert("part", part);


    // 构建 JSON 文档
    QJsonDocument document;
    document.setObject(json);
    QByteArray byteArray = document.toJson(QJsonDocument::Compact);
    QString strJson(byteArray);

    qDebug() << strJson;
    m_socket->write(strJson.toUtf8());
}

